﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using NLite.Reflection;
using NLite.Internal;
using NLite.Validation;

namespace NLite.Mapping.Internal
{
    sealed class GenericMapper<TFrom, TTo> : IMapper<TFrom, TTo>, IMapper
    {
        List<Action<TFrom, TTo>> beforeMapHandlers;
        List<Action<TFrom, TTo>> afterMapHandlers;

        readonly MapperBase InnerMapper;
        public GenericMapper(MapperBase innerMapper)
        {
            InnerMapper = innerMapper;
        }

        #region IMapper<TFrom,TTo> Members


        public IMapper<TFrom, TTo> IgnoreCase(bool flag)
        {
            InnerMapper._Info.IgnoreCase = flag;
            return this;
        }

        public IMapper<TFrom, TTo> IgnoreUnderscore(bool flag)
        {
            InnerMapper._Info.IgnoreUnderscore = flag;
            return this;
        }

        public IMapper<TFrom, TTo> IgnoreSourceMembers(Func<Type, IEnumerable<string>> filter)
        {
            Assumes.NotNull<Func<Type, IEnumerable<string>>>(filter);

            if (InnerMapper._Info.ignoreSourceMembers == null)
                InnerMapper._Info.ignoreSourceMembers = new HashSet<string>();

            foreach (var item in filter(InnerMapper._Info.From))
                InnerMapper._Info.ignoreSourceMembers.Add(item);
            return this;
        }


        public IMapper<TFrom, TTo> IgnoreDestinationMembers(Func<Type, IEnumerable<string>> filter)
        {
            Assumes.NotNull<Func<Type, IEnumerable<string>>>(filter);

            if (InnerMapper._Info.ignoreDestinationMembers == null)
                InnerMapper._Info.ignoreDestinationMembers = new HashSet<string>();

            foreach (var item in filter(InnerMapper._Info.To))
                InnerMapper._Info.ignoreDestinationMembers.Add(item);
            return this;
        }

        public IMapper<TFrom, TTo> IgnoreSourceMember(Expression<Func<TFrom, object>> member)
        {
            Assumes.NotNull<Expression<Func<TFrom, object>>>(member);
            var memberInfo = member.FindProperty();

            if (InnerMapper._Info.ignoreSourceMembers == null)
                InnerMapper._Info.ignoreSourceMembers = new HashSet<string>();

            InnerMapper._Info.ignoreSourceMembers.Add(memberInfo.Name);
            return this;
        }

        public IMapper<TFrom, TTo> IgnoreDestinationMember(Expression<Func<TTo, object>> member)
        {
            Assumes.NotNull<Expression<Func<TTo, object>>>(member);
            var memberInfo = member.FindProperty();

            if (InnerMapper._Info.ignoreDestinationMembers == null)
                InnerMapper._Info.ignoreDestinationMembers = new HashSet<string>();

            InnerMapper._Info.ignoreDestinationMembers.Add(memberInfo.Name);
            return this;
        }

        public IMapper<TFrom, TTo> ForMember(Expression<Func<TTo, object>> destinationMember,
                                                                   Func<TFrom, object> memberOptions)
        {
            Assumes.NotNull<Expression<Func<TTo, object>>>(destinationMember);
            Assumes.NotNull<Func<TFrom, object>>(memberOptions);

            var memberInfo = destinationMember.FindProperty();

            if (InnerMapper._Info.memberMappings == null)
                InnerMapper._Info.memberMappings = new Dictionary<System.Reflection.MemberInfo, Delegate>();

            InnerMapper._Info.memberMappings.Add(memberInfo, memberOptions);

            return this;
        }

        public IMapper<TFrom, TTo> MatchMembers(Func<string, string, bool> membersMatcher)
        {
            Assumes.NotNull<Func<string, string, bool>>(membersMatcher);
            InnerMapper._Info.membersMatcher = membersMatcher;
            return this;
        }

        public IMapper<TFrom, TTo> ConvertUsing<From, To>(Func<From, To> converter)
        {
            Assumes.NotNull<Func<From, To>>(converter);

            var key = typeof(From).FullName + "->" + typeof(To).FullName;

            if (InnerMapper._Info.converters == null)
                InnerMapper._Info.converters = new Dictionary<string, Delegate>();

            if (!InnerMapper._Info.converters.ContainsKey(key))
                InnerMapper._Info.converters.Add(key, converter);

            return this;
        }

        public IMapper<TFrom, TTo> BeforeMap(Action<TFrom, TTo> handler)
        {
            if (beforeMapHandlers == null)
                beforeMapHandlers = new List<Action<TFrom, TTo>>(1);
            beforeMapHandlers.Add(handler);
            return this;
        }

        public IMapper<TFrom, TTo> AfterMap(Action<TFrom, TTo> handler)
        {
            if (afterMapHandlers == null)
                afterMapHandlers = new List<Action<TFrom, TTo>>(1);
            afterMapHandlers.Add(handler);
            return this;
        }

        #endregion

        public IMapperInfo Info { get { return InnerMapper._Info; } }
        public IValidationResult State { get { return InnerMapper.State; } }

        void IMapper.Map(ref object from, ref object to)
        {
            InnerMapper.State = new Validation.DataAnnotations.ValidationResult();
            InnerMapper.Map(ref from,ref to);
        }

        public TTo Map(TFrom from)
        {
            var to = default(TTo);
            InternalMap(ref from, ref to);
            return to;
        }

        public void Map(TFrom from, ref TTo to)
        {
            InternalMap(ref from, ref to);
        }

        private void InternalMap(ref TFrom from, ref TTo to)
        {
            InnerMapper.State = new Validation.DataAnnotations.ValidationResult();

            var fromType = InnerMapper._Info.From;
            var toType = InnerMapper._Info.To;

            object tmpFrom = from;
            object tmpTo = to;

            //如果目标类型是Object类型，直接返回 
            if (toType == Types.Object)
            {
                tmpTo = from;
                to = (TTo)tmpTo;
                return;
            }

            //如果原类型是空
            if (from == null)
            {
                if (toType.IsClass || toType.IsNullable())
                    return;

                if(tmpTo == null)
                    to = (TTo)ObjectCreator.Create(toType);//创建值类型
                return;
            }
            
            if(object.Equals(DBNull.Value,from))
            {
            	   to = (TTo)ObjectCreator.Create(toType);//创建值类型
                return;
            }

            if (toType == Types.Type)
            {
                tmpTo = (Type)tmpFrom;
                to = (TTo)tmpTo;
                return;
            }

            if (toType.IsAssignableFrom(fromType))
            {
                tmpTo = from;
                to = (TTo)tmpTo;
                return;
            }

            if (toType == Types.Guid)
            {
                
                if (Types.ByteEnumerable.IsAssignableFrom(fromType))
                    tmpTo = new Guid( (from as IEnumerable<byte>).ToArray());
                else
                    tmpTo = new Guid(from.ToString());

                to = (TTo)tmpTo;
                return;
            }

            if (beforeMapHandlers != null)
                foreach(var item in beforeMapHandlers)
                    item(from, to);

            InnerMapper.Map(ref tmpFrom, ref tmpTo);


            from = (TFrom)tmpFrom;
            to = (TTo)tmpTo;

            if (afterMapHandlers != null)
                foreach (var item in afterMapHandlers)
                    item(from, to);
        }
    }
}
